﻿namespace NJQuery {
	using System;
	using System.Web.UI;
	using System.ComponentModel;

	public class ResizableOptions : IClientHash {


		#region Constants: Interaction property defaults

		private const bool defaultAnimate = false;
		private const string defaultAnimateDurationString = "Slow";
		private const string defaultAnimateEasing = "swing";
		private const string defaultAspectRatioString = "false";
		private const bool defaultAutoHide = false;
		private const int defaultDistance = 1;
		private const int defaultDelay = 0;
		private const bool defaultGhost = false;
		private const string defaultGridString = "";
		private const int defaultMinHeight = 10;
		private const int defaultMinWidth = 10;
		private const bool defaultEnabled = false;

		#endregion


		#region Static Fields: Interaction property defaults

		//If changing this value, be sure to change the corresponding string constant to match
		private static readonly Duration defaultAnimateDuration = Duration.Slow;
		private static readonly AspectRatio defaultAspectRatio = AspectRatio.Unconstrained;
		private static readonly Grid defaultGrid = Grid.None;

		#endregion


		#region Fields

		StateBag viewState;

		#endregion


		#region Constructors

		/// <summary>
		/// Initializes a new instance of the <see cref="ResizableOptions"/> class.
		/// </summary>
		/// <param name="viewState">The container control's <see cref="StateBag"/> (ViewState) object.</param>
		/// <exception cref="ArgumentNullException">View State cannot be null.</exception>
		public ResizableOptions(StateBag viewState) {
			if(viewState == null) throw new ArgumentNullException("viewState");

			this.viewState = viewState;
		}

		#endregion


		#region Properties: Interaction initialization options

		/// <summary>
		/// Animates to the final size after resizing.
		/// A CSS style must be specified for the class "ui-resizable-helper" in order to see the pending resize.
		/// This property maps directly to the "animate" option when the implemented jQuery UI interaction is initialized.
		/// </summary>
		[DefaultValue(defaultAnimate)]
		public virtual bool Animate {
			get { return viewState["Resizable.Animate"] as bool? ?? defaultAnimate; }
			set { viewState["Resizable.Animate"] = value; }
		}

		/// <summary>
		/// Duration time for animating, in milliseconds. Other possible values: slow, normal, fast.
		/// This property is directly mapped to the "animateDuration" option when the jQuery UI interaction is initialized.
		/// </summary>
		[DefaultValue(typeof(Duration), defaultAnimateDurationString)]
		public virtual Duration AnimateDuration {
			get { return viewState["Resizable.AnimateDuration"] as Duration? ?? defaultAnimateDuration; }
			set { viewState["Resizable.AnimateDuration"] = value; }
		}

		//TODO: You know the drill... Improve Effects engine to be like themes...
		/// <summary>
		/// Easing effect for animating.
		/// This property is directly mapped to the "animateEasing" option when the jQuery UI interaction is initialized.
		/// </summary>
		[DefaultValue(defaultAnimateEasing)]
		public virtual string AnimateEasing {
			get { return viewState["Resizable.AnimateEasing"] as string ?? defaultAnimateEasing; }
			set { viewState["Resizable.AnimateEasing"] = value; }
		}

		/// <summary>
		/// If set to true (Current), resizing is constrained by the original aspect ratio. Otherwise a custom aspect ratio can be specified, such as 9 / 16, or 0.5.
		/// This property is directly mapped to the "aspectRatio" option when the jQuery UI interaction is initialized.
		/// </summary>
		[DefaultValue(typeof(AspectRatio), defaultAspectRatioString)]
		public virtual AspectRatio AspectRatio {
			get { return viewState["Resizable.AspectRatio"] as AspectRatio? ?? defaultAspectRatio; }
			set { viewState["Resizable.AspectRatio"] = value; }
		}

		/// <summary>
		/// If set to true, automatically hides the handles except when the mouse hovers over the element.
		/// This property maps directly to the "autoHide" option when the implemented jQuery UI interaction is initialized.
		/// </summary>
		[DefaultValue(defaultAutoHide)]
		public virtual bool AutoHide {
			get { return viewState["Resizable.AutoHide"] as bool? ?? defaultAutoHide; }
			set { viewState["Resizable.AutoHide"] = value; }
		}

		/// <summary>
		/// Tolerance, in milliseconds, for when resizing should start. If specified, resizing will not start until after mouse is moved beyond duration. This can help prevent unintended resizing when clicking on an element.
		/// This property maps directly to the "delay" option when the implemented jQuery UI interaction is initialized.
		/// </summary>
		[DefaultValue(defaultDelay)]
		public virtual int Delay {
			get { return viewState["Resizable.Delay"] as int? ?? defaultDelay; }
			set { viewState["Resizable.Delay"] = value; }
		}

		/// <summary>
		/// Tolerance, in pixels, for when resizing should start. If specified, resizing will not start until after mouse is moved beyond distance. This can help prevent unintended resizing when clicking on an element.
		/// This property maps directly to the "distance" option when the implemented jQuery UI interaction is initialized.
		/// </summary>
		[DefaultValue(defaultDistance)]
		public virtual int Distance {
			get { return viewState["Resizable.Distance"] as int? ?? defaultDistance; }
			set { viewState["Resizable.Distance"] = value; }
		}

		/// <summary>
		/// If set to true, automatically hides the handles except when the mouse hovers over the element.
		/// This property maps directly to the "ghost" option when the implemented jQuery UI interaction is initialized.
		/// </summary>
		[DefaultValue(defaultGhost)]
		public virtual bool Ghost {
			get { return viewState["Resizable.Ghost"] as bool? ?? defaultGhost; }
			set { viewState["Resizable.Ghost"] = value; }
		}

		/// <summary>
		/// This is the CSS class that will be added to a proxy element to outline the resize during the drag of the resize handle. Once the resize is complete, the original element is sized.
		/// This property maps directly to the "helper" option when the implemented jQuery UI interaction is initialized.
		/// </summary>
		[DefaultValue("")]
		public virtual string Helper {
			get { return viewState["Resizable.Helper"] as string ?? string.Empty; }
			set { viewState["Resizable.Helper"] = value; }
		}

		/// <summary>
		/// Snaps the resizing element to a grid, every x and y pixels.
		/// This property is directly mapped to the "grid" option when the jQuery UI interaction is initialized.
		/// </summary>
		[DefaultValue(typeof(Grid), defaultGridString)]
		public virtual Grid Grid {
			get { return viewState["Resizable.Grid"] as Grid? ?? defaultGrid; }
			set { viewState["Resizable.Grid"] = value; }
		}

		/// <summary>
		/// The maximum height to which the panel can be resized, in pixels.
		/// This property maps directly to the "maxHeight" option when the jQuery UI interaction is initialized.
		/// </summary>
		[DefaultValue(null)]
		public virtual int? MaxHeight {
			get { return viewState["Resizable.MaxHeight"] as int?; }
			set { viewState["Resizable.MaxHeight"] = value; }
		}

		/// <summary>
		/// The maximum width to which the panel can be resized, in pixels.
		/// This property maps directly to the "maxWidth" option when the jQuery UI interaction is initialized.
		/// </summary>
		[DefaultValue(null)]
		public virtual int? MaxWidth {
			get { return viewState["Resizable.MaxWidth"] as int?; }
			set { viewState["Resizable.MaxWidth"] = value; }
		}

		/// <summary>
		/// The minimum height to which the panel can be resized, in pixels.
		/// This property maps directly to the "minHeight" option when the jQuery UI interaction is initialized.
		/// </summary>
		[DefaultValue(defaultMinHeight)]
		public virtual int MinHeight {
			get { return viewState["Resizable.MinHeight"] as int? ?? defaultMinHeight; }
			set { viewState["Resizable.MinHeight"] = value; }
		}

		/// <summary>
		/// The minimum width to which the panel can be resized, in pixels.
		/// This property maps directly to the "minWidth" option when the jQuery UI interaction is initialized.
		/// </summary>
		[DefaultValue(defaultMinWidth)]
		public virtual int MinWidth {
			get { return viewState["Resizable.MinWidth"] as int? ?? defaultMinWidth; }
			set { viewState["Resizable.MinWidth"] = value; }
		}

		#endregion


		#region Properties: Additional

		[DefaultValue(defaultEnabled)]
		public virtual bool Enabled {
			get { return viewState["Resizable.Enabled"] as bool? ?? defaultEnabled; }
			set { viewState["Resizable.Enabled"] = value; }
		}

		#endregion


		#region Methods: Rendering hooks

		/// <summary>
		/// Creates the properties object that will be rendered as the jQuery UI initialization options.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="properties">A <see cref="PropertyCollection"/> object that will become the jQuery UI initialization options.</param>
		public virtual void AddInitProperties(PropertyCollection properties) {

			bool animate = Animate;
			if(animate != defaultAnimate) properties.AddValue("animate", animate);

			Duration animateDuration = AnimateDuration;
			if(animateDuration != defaultAnimateDuration) properties.AddValue("animateDuration", animateDuration);

			string animateEasing = AnimateEasing;
			if(animateEasing != defaultAnimateEasing) properties.AddValue("animateEasing", animateEasing);

			AspectRatio aspectRatio = AspectRatio;
			if(aspectRatio != defaultAspectRatio) properties.AddValue("aspectRatio", aspectRatio);

			bool autoHide = AutoHide;
			if(autoHide != defaultAutoHide) properties.AddValue("autoHide", autoHide);

			int delay = Delay;
			if(delay != defaultDelay) properties.AddValue("delay", delay);

			int distance = Distance;
			if(distance != defaultDistance) properties.AddValue("distance", distance);

			bool ghost = Ghost;
			if(ghost != defaultGhost) properties.AddValue("ghost", ghost);

			Grid grid = Grid;
			if(grid != defaultGrid) properties.AddValue("grid", grid);

			string helper = Helper;
			if(!string.IsNullOrEmpty(helper)) properties.AddValue("helper", helper);

			int? maxHeight = MaxHeight;
			if(maxHeight.HasValue) properties.AddValue("maxHeight", maxHeight.Value);

			int? maxWidth = MaxWidth;
			if(maxWidth.HasValue) properties.AddValue("maxWidth", maxWidth.Value);

			int minHeight = MinHeight;
			if(minHeight != defaultMinHeight) properties.AddValue("minHeight", minHeight);

			int minWidth = MinWidth;
			if(minWidth != defaultMinWidth) properties.AddValue("minWidth", minWidth);

		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="indent">The client source indentation level.</param>
		/// <returns>The client script value.</returns>
		public string ToClientSource(int indent) {
			PropertyCollection childProperties = new PropertyCollection(indent);
			AddInitProperties(childProperties);
			return childProperties.ToClientSource();
		}

		#endregion


	}

}